/*  _______________________________________________________________________

    DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
    Copyright 2014-2022
    National Technology & Engineering Solutions of Sandia, LLC (NTESS).
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Dakota directory.
    _______________________________________________________________________ */

//- Class:       rSQPOptimizer
//- Description: Implementation code for the rSQPOptimizer class
//- Owner:       Roscoe Bartlett
//- Checked by:

#include "system_defs.h"
#include "rSQPOptimizer.H"
#include "ProblemDescDB.H"

#include "MoochoPack/configurations/MoochoSolver.hpp"
#include "DenseLinAlgPack/src/LinAlgOpPack.hpp"
#include "ThrowException.hpp"

static const char rcsId[]="@(#) $Id: rSQPOptimizer.C 7029 2010-10-22 00:17:02Z mseldre $";

namespace {

DenseLinAlgPack::DVectorSlice my_vec_view( Dakota::RealVector& v )
{
	return DenseLinAlgPack::DVectorSlice( &const_cast<Dakota::RealVector&>(v)[0], v.length() );
}

const DenseLinAlgPack::DVectorSlice my_vec_view( const Dakota::RealVector& v )
{
	return DenseLinAlgPack::DVectorSlice( &const_cast<Dakota::RealVector&>(v)[0], v.length() );
}

} // end namespace

//
// NLPDakota
//

namespace NLPInterfacePack {

// Constructors / initializers

NLPDakota::NLPDakota(
	Dakota::Model               *model
	,rSQPOptimizer            *dakota_rsqp_opt
	,size_type                num_lin_eq
	,size_type                num_nonlin_eq
	,size_type                num_lin_ineq
	,size_type                num_nonlin_ineq
	)
	:model_(model)
	,dakota_rsqp_opt_(dakota_rsqp_opt)
	,multi_obj_weights_(dakota_rsqp_opt->primary_response_fn_weights())
	,num_lin_eq_(num_lin_eq)
	,num_nonlin_eq_(num_nonlin_eq)
	,num_lin_ineq_(num_lin_ineq)
	,num_nonlin_ineq_(num_nonlin_ineq)
	,is_initialized_(false)
	,n_orig_(0)
	,f_orig_updated_(false)
	,c_orig_updated_(false)
	,h_orig_updated_(false)
	,Gf_orig_updated_(false)
	,Gc_orig_updated_(false)
	,Gh_orig_updated_(false)
{
	THROW_EXCEPTION(
		model_ == NULL, std::logic_error
		,"NLPDakota::NLPDakota(model): Error, model can not be NULL!"
		);
	// Get a representation for an invalid value
	typedef std::numeric_limits<value_type> nl_t;
	invalid_func_value_ = nl_t::has_infinity
		? nl_t::infinity()
		: ( nl_t::has_quiet_NaN
			? nl_t::quiet_NaN()
			: nl_t::max()        // If Inf and Nan are not supported just put in a large value
			);
	// Get the dummy value for failure
	const Dakota::RealVector &failure_values = model_->problem_description_db().get_rdv("interface.failure_capture.recovery_fn_vals");
	if(failure_values.length()) {
		// Dakota will return  failure_values in case the simulation failes
		dakota_failed_value_ = failure_values[0]; // Just watch out for the first one
	}
	else { // Dakota will not return a failed value
		dakota_failed_value_ = invalid_func_value_; // This should not be generated by accident
	}
}

// Overridden public members from NLP

void NLPDakota::initialize(bool test_setup)
{

	if(is_initialized_) {
		NLPSerialPreprocessExplJac::initialize(test_setup);
		return;
	}

	//
	// Get the dimensions of the problem
	//
	
	const int num_responces = model_->num_functions();
	num_objectives_         = dakota_rsqp_opt_->num_objectives();
	
	n_orig_  = model_->cv();
	m_orig_  = num_lin_eq_   + num_nonlin_eq_;
	mI_orig_ = num_lin_ineq_ + num_nonlin_ineq_;

	//
	// Get the data for the problem
	//
	
	// xinit, xl, xu
	xinit_orig_  = my_vec_view(model_->continuous_variables());
	xl_orig_     = my_vec_view(model_->continuous_lower_bounds());
	xu_orig_     = my_vec_view(model_->continuous_upper_bounds());
	
	has_var_bounds_ = true; // We will assume this here

	// hl, hu
	hl_orig_.resize(mI_orig_);
	hu_orig_.resize(mI_orig_);
	if(num_lin_ineq_) {
		hl_orig_(1,num_lin_ineq_)          = my_vec_view(dakota_rsqp_opt_->lin_ineq_lb());
		hu_orig_(1,num_lin_ineq_)          = my_vec_view(dakota_rsqp_opt_->lin_ineq_ub());
	}
	if(num_nonlin_ineq_) {
		hl_orig_(num_lin_ineq_+1,mI_orig_) = my_vec_view(dakota_rsqp_opt_->nonlin_ineq_lb());
		hu_orig_(num_lin_ineq_+1,mI_orig_) = my_vec_view(dakota_rsqp_opt_->nonlin_ineq_ub());
	}

	// Gc, Gh
	Gc_orig_nz_ = n_orig_ * m_orig_;
	Gh_orig_nz_ = n_orig_ * mI_orig_;

	// Dakota interface stuff
	dakota_x_.reshape(n_orig_);
	dakota_asv_.reshape(num_responces);
	dakota_functions_.reshape(num_responces);

	is_initialized_ = true;
	NLPSerialPreprocessExplJac::initialize(test_setup);

//	throw std::logic_error("NLPDakota::initialize(): End, dam it!");

}

bool NLPDakota::is_initialized() const
{
	return is_initialized_;
}

value_type NLPDakota::max_var_bounds_viol() const
{
	return 1.0; // I don't know what to assume?
}

void NLPDakota::set_multi_calc(bool multi_calc) const
{
	multi_calc_ = multi_calc;
}

bool NLPDakota::multi_calc() const
{
	return multi_calc_;
}

// Overridden from NLPVarReductPerm

bool NLPDakota::nlp_selects_basis() const
{
	return false; // We don't know a basis
}

// Overridden protected methods from NLPSerialPreprocess

bool NLPDakota::imp_nlp_has_changed() const
{
	return false;  // There is no way to modify the nlp accept though the constructor
}

size_type NLPDakota::imp_n_orig() const
{
	return n_orig_;
}

size_type NLPDakota::imp_m_orig() const
{
	return m_orig_;
}

size_type NLPDakota::imp_mI_orig() const
{
	return mI_orig_;
}

const DVectorSlice NLPDakota::imp_xinit_orig() const
{
	return xinit_orig_;
}

bool NLPDakota::imp_has_var_bounds() const
{
	return has_var_bounds_;
}

const DVectorSlice NLPDakota::imp_xl_orig() const
{
	return xl_orig_;
}

const DVectorSlice NLPDakota::imp_xu_orig() const
{
	return xu_orig_;
}

const DVectorSlice NLPDakota::imp_hl_orig() const
{
	return hl_orig_;
}

const DVectorSlice NLPDakota::imp_hu_orig() const
{
	return hu_orig_;
}

void NLPDakota::imp_calc_f_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_F,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_c_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_C,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_h_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_H,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_Gf_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ObjGradInfoSerial     &obj_grad_info
	) const
{
	imp_calc_point(CALC_GF,x_full,newx,NULL,&obj_grad_info,NULL);
}

void NLPDakota::imp_report_orig_final_solution(
	const DVectorSlice      &x_orig
	,const DVectorSlice     *lambda_orig
	,const DVectorSlice     *lambdaI_orig
	,const DVectorSlice     *nu_orig
	,bool                  is_optimal
	) const
{
	// I don't know where to send this stuff?  I could save it in a
	// file or something for later use.
}

// Overridden protected methods from NLPSerialPreprocessExplJac

size_type NLPDakota::imp_Gc_nz_orig() const
{
	return Gc_orig_nz_;
}

size_type NLPDakota::imp_Gh_nz_orig() const
{
	return Gh_orig_nz_;
}

void NLPDakota::imp_calc_Gc_orig(
	const DVectorSlice& x_full, bool newx
	, const FirstOrderExplInfo& first_order_expl_info
	) const
{
	imp_calc_point(CALC_GC,x_full,newx,NULL,NULL,&first_order_expl_info);
}

void NLPDakota::imp_calc_Gh_orig(
	const DVectorSlice& x_full, bool newx
	, const FirstOrderExplInfo& first_order_expl_info
	) const
{
	imp_calc_point(CALC_GH,x_full,newx,NULL,NULL,&first_order_expl_info);
}

// Private member functions

void NLPDakota::assert_is_initialized() const
{
	THROW_EXCEPTION(
		!is_initialized_, std::logic_error
		,"NLPDakota::assert_is_initialized(): Error, the object has not been initialized properly!"
		);
}

void NLPDakota::imp_calc_point(
	ERequiredFunc                required
	,const DVectorSlice           &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   *zero_order_info
	,const ObjGradInfoSerial     *obj_grad_info
	,const FirstOrderExplInfo    *first_order_expl_info
	) const
{
	using LinAlgOpPack::Vp_StV;
	typedef FirstOrderExplInfo::val_t     G_val_t;
	typedef FirstOrderExplInfo::ivect_t   G_ivect_t;
	typedef FirstOrderExplInfo::jvect_t   G_jvect_t;

	Cout << "\nEntering NLPDakota::imp_calc_point(...) ...\n";

//	throw std::logic_error("NLPDakota::imp_calc_point(): End, dam it!");

	if( newx ) {
		f_orig_updated_ = false;
		c_orig_updated_ = false;
		h_orig_updated_ = false;
		Gf_orig_updated_ = false;
		Gc_orig_updated_ = false;
		Gh_orig_updated_ = false;
		my_vec_view(dakota_functions_) = 0.0;
	}

	const DVectorSlice x_orig = x_full(1,n_orig_);

	//
	// Determine which quantities to compute
	//

	const bool calc_f  = ( required==CALC_F  || multi_calc_ )                                      && !f_orig_updated_;
	const bool calc_c  = ( required==CALC_C  || multi_calc_ )                                      && !c_orig_updated_;
	const bool calc_h  = ( required==CALC_H  || multi_calc_ )                                      && !h_orig_updated_;
	const bool calc_Gf = ( required==CALC_GF || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gf_orig_updated_;
	const bool calc_Gc = ( required==CALC_GC || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gc_orig_updated_;
	const bool calc_Gh = ( required==CALC_GH || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gh_orig_updated_;

	if( !calc_f && !calc_c && !calc_h && !calc_Gf && !calc_Gc && !calc_Gh )
		return; // Everything is already computed at this point!

	value_type      *f         = NULL;
	DVector         *c         = NULL;
	DVector         *h         = NULL;
	DVector         *Gf        = NULL;
	G_val_t         *Gc_val    = NULL;
	G_ivect_t       *Gc_ivect  = NULL;
	G_jvect_t       *Gc_jvect  = NULL;
	G_val_t         *Gh_val    = NULL;
	G_ivect_t       *Gh_ivect  = NULL;
	G_jvect_t       *Gh_jvect  = NULL;

	if(zero_order_info) {
		if(calc_f) f = zero_order_info->f;
		if(calc_c) c = zero_order_info->c;
		if(calc_h) h = zero_order_info->h;
	}
	else if(obj_grad_info) {
		if(calc_f)  f  = obj_grad_info->f;
		if(calc_c)  c  = obj_grad_info->c;
		if(calc_h)  h  = obj_grad_info->h;
		if(calc_Gf) Gf = obj_grad_info->Gf;
	}
	else if(first_order_expl_info) {
		if(calc_f)  f  = first_order_expl_info->f;
		if(calc_c)  c  = first_order_expl_info->c;
		if(calc_h)  h  = first_order_expl_info->h;
		if(calc_Gf) Gf = first_order_expl_info->Gf;
		if(calc_Gc) {
			Gc_val   = first_order_expl_info->Gc_val;
			Gc_ivect = first_order_expl_info->Gc_ivect;
			Gc_jvect = first_order_expl_info->Gc_jvect;
		}
		if(calc_Gh) {
			Gh_val   = first_order_expl_info->Gh_val;
			Gh_ivect = first_order_expl_info->Gh_ivect;
			Gh_jvect = first_order_expl_info->Gh_jvect;
		}
	}
	else {
		THROW_EXCEPTION(
			!(zero_order_info || obj_grad_info || first_order_expl_info), std::logic_error
			,"NLPDakota::imp_calc_point(): Error!"
			);
	}

	//
	// Set the Dakota active-set vector.
	//
	// Dakota sorts the responce functions as general inequalities first
	// and general equalities second.
	//

	std::fill_n( &dakota_asv_[0], dakota_asv_.length(), 0 );

	const int num_obj_funcs = multi_obj_weights_.length();

	if( calc_f || calc_Gf ) {
		std::fill_n(
			&dakota_asv_[0], num_obj_funcs
			,(calc_f  ? 1 : 0) + (calc_Gf ? 2 : 0)
			);
	}
	if( ( calc_c || calc_Gc ) && num_nonlin_eq_ ) {
		std::fill_n(
			&dakota_asv_[num_obj_funcs + num_nonlin_ineq_], num_nonlin_eq_
			,(calc_c  ? 1 : 0) + (calc_Gc ? 2 : 0)
			);
	}
	if( ( calc_h || calc_Gh ) && num_nonlin_ineq_ ) {
		std::fill_n(
			&dakota_asv_[num_obj_funcs], num_nonlin_ineq_
			,(calc_h  ? 1 : 0) + (calc_Gh ? 2 : 0)
			);
	}

	//
	// Compute the nonlinear functions
	//

	my_vec_view(dakota_x_) = x_orig;
	model_->continuous_variables(dakota_x_);
	model_->compute_response(dakota_asv_);
	const Dakota::Response&   local_response           = model_->current_response();
	const Dakota::RealVector& local_function_values    = local_response.function_values();
	const Dakota::RealMatrix& local_function_gradients = local_response.function_gradients();

	// Look for failed response
	const bool eval_failed =  (local_function_values[0] == dakota_failed_value_);

	//
	// Map to the NLP objects
	//

	if(!eval_failed) {

		//
		// The evaluation succeeded so copy the values
		//

		// f, Gf
		if(calc_f)  *f  = 0.0;
		if(calc_Gf) *Gf = 0.0;
		for( int i = 0; i < num_obj_funcs; ++i ) {
			if(calc_f) {
				*f  += multi_obj_weights_[i] * local_function_values[i];
				dakota_functions_[i] = local_function_values[i]; // Save in dakota_functions
			}
			if(calc_Gf) {
				Vp_StV(
					&(*Gf)(1,n_orig_), multi_obj_weights_[i]
					,DVectorSlice(&const_cast<Dakota::RealMatrix&>(local_function_gradients)[i][0],n_orig_)
					);
			}
		}
		if(calc_Gf && Gf->dim() > n_orig_ )
			(*Gf)(n_orig_+1,Gf->dim()) = 0.0; // Zero for slack variables
		
		// c = [ A_lin_eq' * x - lin_eq_targ                                                                  ]
		//     [ response(num_obj_funcs+num_nonlin_ineq+1:num_obj_funs+num_nonlin_ineq+num_nonlin_eq) - nonlin_eq_targ ]
		if( calc_c ) {
			if( num_lin_eq_ ) {
				DVectorSlice c_lin = (*c)(1,num_lin_eq_);
				V_mV( &c_lin, my_vec_view(dakota_rsqp_opt_->lin_eq_targ()) );
				const Dakota::RealMatrix  &lin_eq_jac = dakota_rsqp_opt_->lin_eq_jac();
				for( int j = 0; j < num_lin_eq_; ++j ) {
					for( int i = 0; i < n_orig_; ++i ) {
						c_lin[j] += lin_eq_jac[j][i] * x_orig[i];
					}
				}
			}
			if( num_nonlin_eq_ ) {
				const value_type
					*nonlin_eq_ptr = &const_cast<Dakota::RealVector&>(local_function_values)[num_obj_funcs+num_nonlin_ineq_];
				V_VmV(
					&(*c)(num_lin_eq_+1,num_lin_eq_+num_nonlin_eq_)
					,DVectorSlice(const_cast<value_type*>(nonlin_eq_ptr),num_nonlin_eq_)
					,my_vec_view(dakota_rsqp_opt_->nonlin_eq_targ())
					);
				// Save in dakota_functions
				std::copy( nonlin_eq_ptr, nonlin_eq_ptr + num_nonlin_eq_, &dakota_functions_[num_obj_funcs+num_nonlin_ineq_] );
			}
		}
		
		// h = [ A_lin_ineq' * x                                   ]
		//     [ response(num_obj_funcs+1:num_obj_funs+num_lin_eq) ]
		if( calc_h ) {
			//
			if( num_lin_ineq_ ) {
				DVectorSlice h_lin = (*h)(1,num_lin_ineq_);
				const Dakota::RealMatrix  &lin_ineq_jac = dakota_rsqp_opt_->lin_ineq_jac();
				for( int j = 0; j < num_lin_ineq_; ++j ) {
					for( int i = 0; i < n_orig_; ++i ) {
						h_lin[j] += lin_ineq_jac[j][i] * x_orig[i];
					}
				}
			}
			//
			if( num_nonlin_ineq_ ) {
				const value_type
					*nonlin_ineq_ptr = &const_cast<Dakota::RealVector&>(local_function_values)[num_obj_funcs];
				(*h)(num_lin_ineq_+1,num_lin_ineq_+num_nonlin_ineq_)
					= DVectorSlice(const_cast<value_type*>(nonlin_ineq_ptr),num_nonlin_ineq_);
				// Save in dakota_functions
				std::copy( nonlin_ineq_ptr, nonlin_ineq_ptr + num_nonlin_ineq_, &dakota_functions_[num_obj_funcs] );
			}
		}
		
		// Gc = [ A_lin_eq'                                     ]
		//      [ response.grad[num_nonlin_ineq+1]'             ]
		//      [ ..                                            ]
		//      [ response.grad[num_nonlin_ineq+num_nonlin_eq]' ]
		if( calc_Gc ) {
			index_type nz = 0;
			if( num_lin_eq_ ) {
				const Dakota::RealMatrix  &lin_eq_jac = dakota_rsqp_opt_->lin_eq_jac();
				for( int j = 1; j <= num_lin_eq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gc_val)[nz]   = lin_eq_jac[j-1][i-1];
						(*Gc_ivect)[nz] = i;
						(*Gc_jvect)[nz] = j;
						++nz;
					}
				}
			}
			if( num_nonlin_eq_ ) {
				for( int j = 1; j <= num_nonlin_eq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gc_val)[nz]   = local_function_gradients[num_obj_funcs+num_nonlin_ineq_+j-1][i-1];
						(*Gc_ivect)[nz] = i;
						(*Gc_jvect)[nz] = j + num_lin_eq_;
						++nz;
					}
				}
			}
		}
		
		// Gh = [ A_lin_ineq'                   ]
		//      [ response.grad[1]'             ]
		//      [ ..                            ]
		//      [ response.grad[num_nonlin_eq]' ]
		if( calc_Gh ) {
			index_type nz = 0;
			if( num_lin_ineq_ ) {
				const Dakota::RealMatrix  &lin_ineq_jac = dakota_rsqp_opt_->lin_ineq_jac();
				for( int j = 1; j <= num_lin_ineq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gh_val)[nz]   = lin_ineq_jac[j-1][i-1];
						(*Gh_ivect)[nz] = i;
						(*Gh_jvect)[nz] = j;
						++nz;
					}
				}
			}
			if( num_nonlin_ineq_ ) {
				for( int j = 1; j <= num_nonlin_ineq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gh_val)[nz]   = local_function_gradients[num_obj_funcs+j-1][i-1];
						(*Gh_ivect)[nz] = i;
						(*Gh_jvect)[nz] = j;
						++nz;
					}
				}
			}
		}
	}
	else {
		//
		// The evaluation failed so just fill everything with
		// and invalid number that will trigger a back tracking
		//

		// f, Gf
		if(calc_f)  *f  = invalid_func_value_;
		if(calc_Gf) *Gf = invalid_func_value_;
		// c
		if(calc_c)  *c = invalid_func_value_;
		// h
		if(calc_h) *h = invalid_func_value_;
		// Gc
		if(calc_Gc) {
			index_type nz = 0;
			for( int j = 1; j <= m_orig_; ++j ) {
				for( int i = 1; i <= n_orig_; ++i ) {
					(*Gc_val)[nz]   = invalid_func_value_;
					(*Gc_ivect)[nz] = i;
					(*Gc_jvect)[nz] = j;
					++nz;
				}
			}
		}
		// Gh
		if( calc_Gh ) {
			index_type nz = 0;
			for( int j = 1; j <= mI_orig_; ++j ) {
				for( int i = 1; i <= n_orig_; ++i ) {
					(*Gh_val)[nz]   = invalid_func_value_;
					(*Gh_ivect)[nz] = i;
					(*Gh_jvect)[nz] = j;
					++nz;
				}
			}
		}

	}

	if(calc_f)   f_orig_updated_  = true;
	if(calc_c)   c_orig_updated_  = true;
	if(calc_h)   h_orig_updated_  = true;
	if(calc_Gf)  Gf_orig_updated_ = true;
	if(calc_Gc)  Gc_orig_updated_ = true;
	if(calc_Gh)  Gh_orig_updated_ = true;

}

} // end namespace NLPInterfacePack

namespace Dakota {

//
// rSQPOptimizer
//

// This constructor accepts a Model
rSQPOptimizer::rSQPOptimizer(Model& model)
	:Optimizer(model)
	,model_(&model)
	 ,nlp_(&model,this
		,numLinearEqConstraints,numNonlinearEqConstraints
		,numLinearIneqConstraints,numNonlinearIneqConstraints)
{}

rSQPOptimizer::~rSQPOptimizer()
{}

void rSQPOptimizer::find_optimum()
{

	//------------------------------------------------------------------
	//     Solve the problem.
	//------------------------------------------------------------------
	
	namespace mmp   = MemMngPack;
	namespace mp    = MoochoPack;
	using mp::MoochoSolver;

	Cout << "\nCalling rSQP++ to solve the NLP! ...\n";

	if(model_->dv()) {
		Cout << "\nWarning!!! The dakota model has " << model_->dv() << " discrete variables "
			 << "associated with it that will be ignored by this optimizer!";
	}
	
	try {
		
		MoochoSolver  solver;

/*
		nlp_.initialize(true);

		// Terminate early!
		Cout << "\nrSQPOptimizer::find_optimum(): Terminate early!\n";
		bestVariablesArray.front().continuous_variables(model_->cv());
		RealVector dummy_func_values(model_->num_functions());
		bestResponseArray.front().function_values(dummy_func_values);
		return; // Don't do anything!
*/

		solver.set_nlp( mmp::rcp(&nlp_,false) );

		std::ofstream Moocho_out("MoochoConsole.out");
		solver.set_console_out(mmp::rcp(&Moocho_out,false));
		solver.set_error_handling(true,mmp::rcp(&Moocho_out,false));

		const MoochoSolver::ESolutionStatus
			solution_status = solver.solve_nlp();

		
	}
	catch(const std::exception& excpt) {
		std::cerr << "\nrSQPOptimizer::find_optimum(): Caught a std::exception: " << excpt.what() << std::endl;
	}
	catch(...) {
		std::cerr << "\nrSQPOptimizer::find_optimum(): Caught an unknown exception: \n";
	}

	//------------------------------------------------------------------
	//     Save the final results.
	//------------------------------------------------------------------
	
	bestVariablesArray.front().continuous_variables(nlp_.dakota_x());     // Just use the last point computed
	bestResponseArray.front().function_values(nlp_.dakota_functions());  // ""

}

} // namespace Dakota
